package com.ody.util.code;

import com.odianyun.db.dao.mapper.CamelCaseColumnMapper;
import com.odianyun.db.dao.mapper.CamelCaseTableMapper;
import com.odianyun.db.dao.mapper.ColumnMapper;
import com.odianyun.db.dao.mapper.TableMapper;
import com.ody.util.code.config.ConfigProperties;
import com.ody.util.code.model.CachedModelProvider;
import com.ody.util.code.model.DatabaseModelProvider;
import com.ody.util.code.model.IModelProvider;
import com.ody.util.code.model.ModelGui;
import java.sql.Connection;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.commons.dbcp2.BasicDataSource;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;

public class CodeConfiguration implements ApplicationContextAware {
	private String propertiesFilePath;
	private boolean runConfigWithGui;
    private boolean runModelWithGui;
	
	private ApplicationContext ctx;
	
	public void setPropertiesFilePath(String propertiesFilePath) {
		this.propertiesFilePath = propertiesFilePath;
	}
	public void setRunConfigWithGui(boolean runConfigWithGui) {
		this.runConfigWithGui = runConfigWithGui;
	}
    public void setRunModelWithGui(boolean runModelWithGui) {
        this.runModelWithGui = runModelWithGui;
    }
	
	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.ctx = applicationContext;
	}
	
	@Bean("codeProperties")
	public ConfigProperties codeProperties() throws Exception {
		return new ConfigProperties(propertiesFilePath, runConfigWithGui);
	}
	
	@Bean
	public IModelProvider modelProvider(@Qualifier("codeProperties") Properties props) throws Exception {
        DatabaseModelProvider provider = new DatabaseModelProvider();
        provider.setDataSource(dataSource(props));
        provider.setProperties(props);
        provider.setTableMapper(tableMapper(props));
        provider.setColumnMapper(columnMapper(props));
        provider.setCatalog(props.getProperty("catalog"));
        if (provider.getCatalog() == null) {
            Connection conn = provider.getDataSource().getConnection();
            try {
                provider.setCatalog(conn.getCatalog());
            } catch (Throwable e) {
                e.printStackTrace();
                // ignore
            } finally {
                conn.close();
            }
        }
        provider.setSchemaPattern(props.getProperty("schemaPattern"));
        provider.setTablePattern(props.getProperty("tablePattern"));
        provider.setColumnPattern(props.getProperty("columnPattern"));
        provider.afterPropertiesSet();
        IModelProvider mp = provider;
        if (runModelWithGui) {
            ModelGui model = new ModelGui(provider);
            model.setProperties(props);
            model.afterPropertiesSet();;
            mp = model;
        }
        return new CachedModelProvider(mp);
    }
	
	@Bean
	public GeneratorBeanFactory generatorBeanFactory(@Qualifier("codeProperties") Properties props) {
		GeneratorBeanFactory factory = new GeneratorBeanFactory();
		factory.setProperties(props);
		return factory;
	}
	
	private DataSource dataSource(Properties props) {
		String beanName = props.getProperty("dataSourceBeanName");
		
		if (beanName != null) {
			try {
				return ctx.getBean(beanName, DataSource.class);	
			} catch (NoSuchBeanDefinitionException e) {
				throw new BeanInitializationException(String.format("没有找到Bean[%s]，请检查dataSourceBeanName配置是否正确，"
						+ "或当前使用的是否是共享SpringApplicationContext模式，非共享模式请去掉dataSourceBeanName配置项", beanName), e);
			}
		}
		
		BasicDataSource dataSource = new BasicDataSource();
		dataSource.setDriverClassName(props.getProperty("jdbc.driver"));
		dataSource.setUrl(props.getProperty("jdbc.url"));
		dataSource.setUsername(props.getProperty("jdbc.username"));
		dataSource.setPassword(props.getProperty("jdbc.password"));
		
		return dataSource;
	}
	
	private TableMapper tableMapper(Properties props) {
		String beanName = props.getProperty("tableMapperBeanName");
		
		if (beanName != null) {
			return ctx.getBean(beanName, TableMapper.class);
		}
		
		CamelCaseTableMapper tableMapper = new CamelCaseTableMapper();
		tableMapper.setEntityPrefix(props.getProperty("tableMapper.tablePrefix", ""));
		tableMapper.setEntitySuffix(props.getProperty("tableMapper.tableSuffix", ""));
		tableMapper.setTablePrefix(props.getProperty("tableMapper.entityPrefix", ""));
		tableMapper.setTableSuffix(props.getProperty("tableMapper.entitySuffix", ""));
		
		return tableMapper;
	}

	private ColumnMapper columnMapper(Properties props) {
		String beanName = props.getProperty("columnMapperBeanName");
		
		if (beanName != null) {
			return ctx.getBean(beanName, ColumnMapper.class);
		}
		
		CamelCaseColumnMapper columnMapper = new CamelCaseColumnMapper();
		columnMapper.setColumnPrefix(props.getProperty("columnMapper.columnPrefix", ""));
		columnMapper.setColumnSuffix(props.getProperty("columnMapper.columnSuffix", ""));
		columnMapper.setFieldPrefix(props.getProperty("columnMapper.fieldPrefix", ""));
		columnMapper.setFieldSuffix(props.getProperty("columnMapper.fieldSuffix", ""));

		return columnMapper;
	}
}
